
#include <BFont>
#include <BPoint>
#include <BPainter>
#include <BMouseEvent>
#include <BStyle>

#include <BTabWidget>
#include "member_BTabWidget.h"
#include "member_BAction.h"
#include "member_BWidget.h"

using namespace BWE;

#define member					(*(member_BTabWidget*)_ptr)
#define member_allocate()		_ptr = new member_BTabWidget(this)
#define member_release()		delete (member_BTabWidget*)_ptr

BTabWidget::BTabWidget(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setBorder(1);
	this->setMargin(1, 1, 1, 1);
}
BTabWidget::~BTabWidget()
{
	member_release();
}

void BTabWidget::setTabPart(Part tabPart)
{
	if (member.tabPart != tabPart)
	{
		member.tabPart = tabPart;
		member.dirty = true;
		this->fresh();
	}
}
Part BTabWidget::tabPart() const
{
	return member.tabPart;
}

bool BTabWidget::setTabClosable(BWidget* widget, bool closable)
{
	auto it = member.items.find(widget);
	if (it.empty())
		return false;
	TabItem& item = *it;
	bool itemClosable = item.closeButton != 0;
	if (itemClosable != closable)
	{
		if (closable)
		{
			item.closeButton = new BButton(Graph_Cross, this);
			item.closeButton->setName("close");
			member.connect(item.closeButton, Signal_Clicked, &member_BTabWidget::slotCloseClicked);
			item.dirty = true;
		}
		else
		{
			member.disconnect(item.closeButton);
			this->removeChild(item.closeButton);
			item.closeButton = 0;
			item.dirty = true;
		}
		member.dirty = true;
		this->fresh();
	}
	return true;
}
bool BTabWidget::tabClosable(BWidget* widget) const
{
	auto it = member.items.find(widget);
	if (it.empty())
		return false;
	TabItem& item = *it;
	return item.closeButton != 0;
}

bool BTabWidget::addTab(BWidget* widget, bool closable)
{
	if (!widget)
		return false;
	if (member.items.contain(widget))
		return true;
	this->addChild(widget);
	widget->hide();
	TabItem item(widget);
	member.items.append(item);
	member.connect(widget, Signal_TitleChanged, &member_BTabWidget::slotTitleChanged);
	member.connect(widget, Signal_TitleIconChanged, &member_BTabWidget::slotTitleChanged);
	if (member.stack.empty())
	{
		this->setCurrentWidget(widget);
	}
	setTabClosable(widget, closable);
	member.dirty = true;
	this->fresh();
	return true;
}
bool BTabWidget::removeTab(BWidget* widget)
{
	if (!widget)
		return false;
	if (member.items.contain(widget))
	{
		BHolder<BWidget> holder = widget;
		setTabClosable(widget, false);
		member.disconnect(widget);
		member.stack.remove(widget);
		member.items.remove(widget);
		this->removeChild(widget);
		this->setCurrentWidget(member.stack.last());
		member.dirty = true;
		emit(Signal_TabClosed, widget);
		this->fresh();
		return true;
	}
	return false;
}
bool BTabWidget::removeTab(const BString& title)
{
	BWidget* widget = this->tab(title);
	return removeTab(widget);
}
bool BTabWidget::removeTab(int tab)
{
	BWidget* widget = member.items(tab).widget;
	return removeTab(widget);
}

bool BTabWidget::clearTabs()
{
	if (member.items.empty())
		return false;
	while (member.items.size())
	{
		removeTab(0);
	}
	this->fresh();
	return true;
}

bool BTabWidget::setCurrentIndex(int index)
{
	BWidget* widget = member.items(index).widget;
	return setCurrentWidget(widget);
}
int BTabWidget::currentIndex() const
{
	BWidget* widget = member.stack.last();
	return member.items.index(widget);
}

bool BTabWidget::setCurrentWidget(BWidget* widget)
{
	if (!widget)
		return false;
	if (member.items.contain(widget))
	{
		BWidget* current = member.stack.last();
		if (current && current != widget)
			current->hide();
		widget->show();
		widget->setRect(this->centerRect());
		if (!member.stack.contain(widget))
			member.stack.append(widget);
		else
			member.stack.lower(widget);
		member.reviseTab();
		emit(Signal_CurrentChanged, widget);
		this->fresh();
		return true;
	}
	return false;
}
bool BTabWidget::setCurrentWidget(const BString& title)
{
	for (auto item : member.items)
	{
		if (item.widget->title() == title)
		{
			return setCurrentWidget(item.widget);
		}
	}
	return false;
}
BWidget* BTabWidget::currentWidget() const
{
	return member.stack.last();
}

void BTabWidget::setBegin(int begin)
{
	member.beginIndex = begin;
}
int BTabWidget::begin()
{
	return member.beginIndex;
}

int BTabWidget::tabCount() const
{
	return member.items.size();
}
BWidget* BTabWidget::tab(int index)
{
	return member.items(index).widget;
}
const BWidget* BTabWidget::tab(int index) const
{
	return member.items(index).widget;
}

BWidget* BTabWidget::tab(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->title() == title)
			return item.widget;
	}
	return 0;
}
const BWidget* BTabWidget::tab(const BString& title) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->name() == title)
			return item.widget;
	}
	return 0;
}

BWidget* BTabWidget::tabOfAsset(const BValue& asset)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->asset() == asset)
			return item.widget;
	}
	return 0;
}
const BWidget* BTabWidget::tabOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->asset() == asset)
			return item.widget;
	}
	return 0;
}

bool BTabWidget::contain(const BWidget* widget) const
{
	return member.items.contain(widget);
}
bool BTabWidget::contain(const BString& title) const
{
	for (const auto& item : member.items)
		if (item.widget->title() == title)
			return true;
	return false;
}

BRect BTabWidget::tabRect(int tab)
{
	if (member.dirty)
	{
		member.freshTabs();
		member.dirty = false;
	}
	return member.items(tab).rect;
}
BRect BTabWidget::tabRect(BWidget* widget)
{
	if (member.dirty)
	{
		member.freshTabs();
		member.dirty = false;
	}
	return member.rectOf(widget);
}

void BTabWidget::setHintTabs(bool hintTabs)
{
	if (member.hintTabs != hintTabs)
	{
		member.hintTabs = hintTabs;
		this->adjust();
	}
}
bool BTabWidget::hintTabs() const
{
	return member.hintTabs;
}

BSize BTabWidget::sizeHint() const
{
	BSize csize;
	for (int i = 0; i < member.items.size(); i++)
	{
		BSize sizeHint = member.items[i].widget->sizeHint();
		if(csize.width() < sizeHint.width())
			csize.width() = sizeHint.width();
		if (csize.height() < sizeHint.height())
			csize.height() = sizeHint.height();
	}
	if (member.hintTabs)
	{
		if (member.dirty)
		{
			member.freshTabs();
			member.dirty = false;
		}
		if (csize.width() < member.tabsSizeHint.width())
			csize.width() = member.tabsSizeHint.width();
	}
	BSize hintSize = this->preferSize(csize);
	return hintSize;
}

void BTabWidget::resizeEvent(const BEvent& event)
{
	BWidget* current = this->currentWidget();
	if (current)
	{
		current->setRect(this->centerRect());
	}
	member.dirty = true;
	BWidget::resizeEvent(event);
}
void BTabWidget::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = this->realStyle();
	if (style)
	{
		const BFont* font = this->font();
		if (font)
		{
			member.arrow->setIconSize(font->size() / 2);
		}
		if (const BStyle* tabStyle = style->sub("tab"))
		{
			const BValue& alignValue = tabStyle->value(Value_Align);
			if (alignValue.valid())
				member.tabAlign = alignValue;

			const BValue& formValue = tabStyle->value(Value_Form);
			if (formValue.valid())
				member.tabForm = formValue;

			const BValue& spacingValue = tabStyle->value(Value_Space);
			if (spacingValue.valid())
				member.tabSpacing = spacingValue;

			const BValue& marginLeftValue = tabStyle->value(Value_Margin_Left);
			if (marginLeftValue.valid())
				member.tabMargin.left() = marginLeftValue;

			const BValue& marginTopValue = tabStyle->value(Value_Margin_Top);
			if (marginTopValue.valid())
				member.tabMargin.top() = marginTopValue;

			const BValue& marginRightValue = tabStyle->value(Value_Margin_Right);
			if (marginRightValue.valid())
				member.tabMargin.right() = marginRightValue;

			const BValue& marginBottomValue = tabStyle->value(Value_Margin_Bottom);
			if (marginBottomValue.valid())
				member.tabMargin.bottom() = marginBottomValue;
		}
		const BValue& iconSizeValue = style->value(Value_Icon_Size);
		if (iconSizeValue.valid())
			member.iconSize = iconSizeValue;

		const BValue& arrowSizeValue = style->value(Value_Arrow_Size);
		if (arrowSizeValue.valid())
		{
			BSize arrowSize = arrowSizeValue;
			member.arrow->setIconSize(arrowSize);
		}

		for (int i = 0; i < member.items.size(); i++)
		{
			member.items[i].dirty = true;
		}
		member.dirty = true;
	}
}
void BTabWidget::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshTabs();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BTabWidget::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	for (int i = member.beginIndex; i < member.items.size(); i++)
	{
		TabItem& item = member.items[i];
		if (!item.visible)
			continue;

		State state = State_Normal;
		StyleRole colorRole = Color_Text;
		if (item.widget == member.stack.last())
		{
			state = State_Selected;
			colorRole = Color_Text_Checked;
			painter.setColor(Color_Tab_Checked);
			painter.fillRect(item.rect);
		}
		else if (i == member.hoverIndex)
		{
			state = State_Hovered;
			colorRole = Color_Text_Hovered;
			painter.setColor(Color_Tab_Hovered);
			painter.fillRect(item.rect);
		}

		if (painter.setColor(colorRole))
		{
			if (item.t_title.size())
				painter.drawText(item.ia_textRect, item.t_title);
			else
				painter.drawText(item.ia_textRect, item.widget->title());
		}

		BIcon* titleIcon = item.widget->titleIcon();
		if (titleIcon)
		{
			BImage* image = titleIcon->image(state);
			painter.drawImage(item.ia_imageRect, image);
		}
	}
}

void BTabWidget::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BTabWidget::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BTabWidget::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		setCurrentIndex(member.hoverIndex);
		this->fresh();
	}
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = true;
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BTabWidget::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BTabWidget::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	int hoverIndex = -1;
	const BPoint& pos = mouseEvent.pos();
	for(int i = member.beginIndex; i < member.items.size(); i++)
	{
		TabItem& item = member.items[i];
		if(item.rect.contain(pos))
		{
			hoverIndex = i;
			break;
		}
	}
	if (member.hoverIndex != hoverIndex)
	{
		member.hoverIndex = hoverIndex;
		this->fresh();
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BTabWidget::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		BWidget* widget = member.items(member.hoverIndex).widget;
		if (mouseEvent.button() == Button_Left)
		{
			emit(Signal_TabClicked, widget);
			return;
		}
		if (mouseEvent.button() == Button_Right)
		{
			emit(Signal_TabRightClicked, widget);
			return;
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
